package io.github.talelin.latticy.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import io.github.talelin.autoconfigure.exception.ForbiddenException;
import io.github.talelin.autoconfigure.exception.NotFoundException;
import io.github.talelin.latticy.bo.LocationByAreaVo;
import io.github.talelin.latticy.common.mybatis.Page;
import io.github.talelin.latticy.dto.location.LocationDTO;
import io.github.talelin.latticy.dto.location.UpdateLocationDTO;
import io.github.talelin.latticy.model.LocationDO;
import io.github.talelin.latticy.mapper.LocationMapper;
import io.github.talelin.latticy.service.LocationService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import io.github.talelin.latticy.service.WareareaService;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.List;
import java.util.Optional;

/**
 * <p>
 * 仓库 服务实现类
 * </p>
 *
 * @author generator@wulongbo
 * @since 2022-06-07
 */
@Service
public class LocationServiceImpl extends ServiceImpl<LocationMapper, LocationDO> implements LocationService {

    @Autowired
    private WareareaService wareareaService;

    @Transactional
    @Override
    public LocationDO createLocation(LocationDTO dto) {
        this.checkCreateLocation(dto);
        String code = wareareaService.getCodeByAreaId(dto.getAreaId());
        Optional.ofNullable(code).orElseThrow(() -> new NotFoundException(18303));
        LocationDO locationDO = new LocationDO(dto);
        locationDO.setCode(code);
        this.baseMapper.insert(locationDO);
        return locationDO;
    }

    @Transactional
    @Override
    public boolean updateLocation(int id, UpdateLocationDTO dto) {
        if (StringUtils.isNotEmpty(dto.getName())) {
            int rows = this.baseMapper.selectCountByIdAndName(id, dto.getName());
            if (rows > 0) {
                throw new ForbiddenException(18501);
            }
        }
        if (dto.getLocationOrder() != null) {
            boolean exist = this.checkLocationOrderExistById(id, dto.getLocationOrder());
            if (exist) {
                LocationDO location = this.baseMapper.selectById(id);
                this.updateLocationOrder(location.getAreaId(), dto.getLocationOrder());
            }
        }
        LocationDO locationDO = new LocationDO(id, dto);
        return this.baseMapper.updateById(locationDO) > 0;
    }

    @Override
    public boolean deleteLocation(int id) {
        return this.baseMapper.deleteById(id) > 0;
    }

    @Override
    public LocationDO getLocationById(int id) {
        LocationDO location = this.baseMapper.selectById(id);
        Optional.ofNullable(location).orElseThrow(() -> new NotFoundException(18502));
        return location;
    }

    @Override
    public List<LocationDO> getLocationAreaList(List<Integer> areaIdList) {
        QueryWrapper<LocationDO> wrapper = new QueryWrapper<>();
        wrapper.lambda().in(LocationDO::getAreaId, areaIdList);
        return this.list(wrapper);
    }

    @Override
    public List<LocationByAreaVo> getLocationByIdList(List<Integer> idList) {
        return this.baseMapper.getLocationByIdList(idList);
    }

    @Override
    public List<LocationDO> getLocationList(LocationDO locationDO) {
        QueryWrapper<LocationDO> wrapper = new QueryWrapper<>();
        wrapper.lambda().eq(locationDO.getAreaId()!=null,LocationDO::getAreaId, locationDO.getAreaId())
                .eq(locationDO.getCode()!=null,LocationDO::getCode, locationDO.getCode())
                .like(StringUtils.isNotBlank(locationDO.getName()),LocationDO::getName, locationDO.getName());
        return this.baseMapper.selectList(wrapper);
    }

    @Override
    public IPage<LocationDO> getLocationPage(Integer page, Integer count, int areaId) {
        Page<LocationDO> pager = new Page<>(page, count);
        QueryWrapper<LocationDO> wrapper = new QueryWrapper<>();
        wrapper.lambda().eq(LocationDO::getAreaId, areaId);
        return this.baseMapper.selectPage(pager, wrapper);
    }

    @Override
    public boolean checkLocationExistByLocationId(int AreaId) {
        QueryWrapper<LocationDO> wrapper = new QueryWrapper<>();
        wrapper.lambda().eq(LocationDO::getAreaId, AreaId);
        return this.count(wrapper) > 0;
    }

    @Override
    public boolean checkLocationOrderExistById(int id, int locationOrder) {
        LocationDO locationDO = this.baseMapper.selectById(id);
        Optional.ofNullable(locationDO).orElseThrow(() -> new NotFoundException(18502));
        return this.checkLocationOrderExistByAreaId(locationDO.getAreaId(), locationOrder);
    }

    @Override
    public boolean checkLocationExistByLocationName(Integer areaId, String name) {
        QueryWrapper<LocationDO> wrapper = new QueryWrapper<>();
        wrapper.lambda().eq(LocationDO::getAreaId, areaId).eq(LocationDO::getName, name);
        return this.count(wrapper)>0;
    }

    @Override
    public boolean checkLocationOrderExistByAreaId(Integer areaId, int locationOrder) {
        QueryWrapper<LocationDO> wrapper = new QueryWrapper<>();
        wrapper.lambda().eq(LocationDO::getAreaId, areaId).eq(LocationDO::getLocationOrder, locationOrder);
        return this.count(wrapper)>0;
    }

    private void checkCreateLocation(LocationDTO dto) {
        boolean exist = wareareaService.checkWareAreaExistById(dto.getAreaId());
        if (!exist) {
            throw new ForbiddenException(18401);
        }
        exist = this.checkLocationExistByLocationName(dto.getAreaId(), dto.getName());
        if (exist) {
            throw new ForbiddenException(18501);
        }
        exist = this.checkLocationOrderExistByAreaId(dto.getAreaId(), dto.getLocationOrder());
        if (exist) {
            this.updateLocationOrder(dto.getAreaId(), dto.getLocationOrder());
        }
    }

    private void updateLocationOrder(Integer areaId, int locationOrder) {
        UpdateWrapper<LocationDO> wrapper = new UpdateWrapper<>();
        wrapper.eq("area_id", areaId).ge("location_order", locationOrder);
        wrapper.setSql("`location_order` = `location_order` + 1");
        this.update(wrapper);
    }
}
